﻿using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using LaunchPad.Core.Dialogs;

namespace LaunchPad.Core.Controls.Tracks
{
    public partial class TrackStackControl : AudioTimeControlBase
    {
        public static readonly DependencyProperty TracksProperty = DependencyProperty.Register(
            "Tracks", typeof (ObservableCollection<Track>), typeof (TrackStackControl),
            new PropertyMetadata(null, OnTracksChanged));

        private ListCollectionView _effectTrackView;
        private ScrollViewer _trackScrollViewer;

        public TrackStackControl()
        {
            InitializeComponent();

            _scrollViewer.ScrollChanged += ScrollViewerScrollChanged;
            _waveView.ScrollChanged += WaveViewScrollChanged;
            SizeChanged += TrackStackControlSizeChanged;
        }

        public ObservableCollection<Track> Tracks
        {
            get { return (ObservableCollection<Track>) GetValue(TracksProperty); }
            set { SetValue(TracksProperty, value); }
        }

        private void TrackStackControlSizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (e.WidthChanged)
                _trackDataGrid.Width = ActualWidth;

            if (e.HeightChanged)
                _cueHelperControl.Height = ActualHeight;
        }

        private void WaveViewScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            FindTrackScollViewer();

            if (_trackScrollViewer != null)
                _trackScrollViewer.ScrollToHorizontalOffset(e.HorizontalOffset);
        }

        private void FindTrackScollViewer()
        {
            if (_trackScrollViewer == null)
            {
                if (_trackDataGrid.Items.Count > 0)
                {
                    var border = VisualTreeHelper.GetChild(_trackDataGrid, 0) as Decorator;
                    if (border != null)
                    {
                        _trackScrollViewer = border.Child as ScrollViewer;
                    }
                }
            }
        }

        private void ScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            _waveView.SetHorizontalOffset(e.HorizontalOffset);
            _cueHelperControl.Position -= e.HorizontalChange;
        }

        private static void OnTracksChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var control = (TrackStackControl) sender;
            var tracks = (ObservableCollection<Track>)e.NewValue;
            control._effectTrackView = new ListCollectionView(tracks);
            if (control._effectTrackView.GroupDescriptions != null)
                control._effectTrackView.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
            control._trackDataGrid.ItemsSource = control._effectTrackView;

            if (e.OldValue != null)
            {
                ((ObservableCollection<Track>) e.OldValue).CollectionChanged -= control.TracksCollectionChanged;
            }

            if (e.NewValue != null)
            {
                if (tracks != null)
                {
                    tracks.CollectionChanged += control.TracksCollectionChanged;
                    foreach (var track in tracks)
                        track.PropertyChanged += control.EffectPropertyChanged;
                }
            }
        }

        private void EffectPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "Group":
                    if (!_effectTrackView.IsEditingItem)
                        _effectTrackView.Refresh();
                    break;
            }
        }

        private void TracksCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (Track track in e.NewItems.OfType<Track>())
                    track.PropertyChanged += EffectPropertyChanged;
            }

            if (e.OldItems != null)
            {
                foreach (var track in e.OldItems.OfType<Track>())
                    track.PropertyChanged -= EffectPropertyChanged;
            }
        }

        private void EffectTrackSelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            foreach (var track in Tracks)
                track.IsSelected = _trackDataGrid.SelectedItems.Contains(track);
        }

        private void AudioTimeControlBaseBeginCueHelperMove(object sender, RoutedEventArgs e)
        {
            _cueHelperControl.Visibility = Visibility.Visible;
        }

        private void AudioTimeControlBaseCueHelperMove(object sender, RoutedEventArgs e)
        {
            var args = (CueHelperMoveRoutedEventArgs)e;

            FindTrackScollViewer();
            double scrollOffset = _trackScrollViewer == null ? 0 : _trackScrollViewer.ContentHorizontalOffset;
            _cueHelperControl.Position = ConvertFromTime(args.Position) - scrollOffset;
        }

        private void AudioTimeControlBaseEndCueHelperMove(object sender, RoutedEventArgs e)
        {
            _cueHelperControl.Visibility = Visibility.Hidden;
        }

        private void ZoomSliderValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            ZoomFactor = _zoomSlider.Value;
            InvalidateArrange();
        }

        private EffectDialog _effectDialog;

        private void EffectTrackMouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var selectedTrack = _trackDataGrid.SelectedItem as Track;

            if (selectedTrack is EffectTrack)
            {
                if (_effectDialog == null)
                {
                    var parentWindow = Window.GetWindow(this);
                    _effectDialog = new EffectDialog
                                        {
                                            DataContext = selectedTrack,
                                            Nodes = selectedTrack.Field.Nodes,
                                            Owner = parentWindow,
                                            WindowStartupLocation = WindowStartupLocation.CenterOwner
                                        };
                    _effectDialog.Show();
                    _effectDialog.Closed += EffectDialogClosed;
                }
            }

            e.Handled = true;
        }

        void EffectDialogClosed(object sender, EventArgs e)
        {
            _effectDialog.Commit();
            _effectDialog = null;
        }

        private void EffectTrackKeyDown(object sender, KeyEventArgs e)
        {
            if (AudioTrack == null)
                return;

            IList selectedItems = _trackDataGrid.SelectedItems;
            foreach (EffectTrack effect in selectedItems)
            {
                effect.TryAddEvent(AudioTrack.Position);
            }
        }

        #region DnD

        private void EffectTrackDragEnter(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent("effectFormat"))
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
            }
        }

        private void EffectTrackDragOver(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent("effectFormat"))
            {
                e.Effects = DragDropEffects.None;
                e.Handled = true;
            }
        }

        private void EffectTrackDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent("effectFormat"))
            {
                var effect = (EffectTrack)e.Data.GetData("effectFormat");

                var row = FindVisualParent<DataGridRow>(e.OriginalSource as UIElement);
                var expander = FindVisualParent<Expander>(e.OriginalSource as UIElement);

                if (row != null)
                {
                    var selectedEffect = row.Item as EffectTrack;
                    if (selectedEffect != null)
                        effect.Group = selectedEffect.Group;
                }
                else if (expander != null)
                {
                    /* Yeah, not great, but .net screwed us here with the group implementation */
                    var header = expander.Header as TextBlock;
                    effect.Group = header.Text;
                }

                if (!Tracks.Contains(effect))
                    Tracks.Add(effect);

                e.Handled = true;
            }
        }


        #endregion

        private static T FindVisualParent<T>(DependencyObject element) where T : UIElement
        {
            var parent = element;
            while (parent != null)
            {
                var correctlyTyped = parent as T;
                if (correctlyTyped != null)
                {
                    return correctlyTyped;
                }

                parent = VisualTreeHelper.GetParent(parent) as UIElement;
            }

            return null;
        }

        private void _sortButton_OnClick(object sender, RoutedEventArgs e)
        {
            _trackDataGrid.UnselectAll();
            Tracks.BubbleSort();
        }
    }
}